using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace AreaBuilder.Forms {
    public partial class frmMobs : Form {
        private readonly AreaHolder ah;
        private readonly int existingVNum = -1;

        public frmMobs(AreaHolder ah, int value) {
            InitializeComponent();

// ReSharper disable DoNotCallOverridableMethodsInConstructor
            changeColor(tabs, BackColor);
// ReSharper restore DoNotCallOverridableMethodsInConstructor

            this.ah = ah;
            existingVNum = value;
        }

        private void frmMobs_Load(object sender, EventArgs e) {
            Funcs.fillCombo(cmbRace, "/types/mobRaces/race");
            Funcs.fillComboType(cmbClass, "/types/classes/class/@type", "/types/classes/class", "");
            Funcs.fillCombo(cmbStart, "/types/stance/type");
            Funcs.fillCombo(cmbDefault, "/types/stance/type");
            Funcs.fillCombo(cmbDamType, "/types/damages/dam");
            Funcs.fillComboType(cmbDamDice, "/types/die/dice/@level", "/types/die/dice/dam", "Level ");
            Funcs.fillComboType(cmbHitDice, "/types/die/dice/@level", "/types/die/dice/hp", "Level ");
            Funcs.fillComboType(cmbManaDice, "/types/die/dice/@level", "/types/die/dice/hp", "Level ");
            Funcs.fillListType(lstAct, "/types/act/flag/@type", "/types/act/flag", "");
            Funcs.fillListType(lstAffect, "/types/affect/flag/@type", "/types/affect/flag", "");
            Funcs.fillListType(lstAff2, "/types/affect2/flag/@type", "/types/affect2/flag", "");
            Funcs.fillListType(lstOffensive, "/types/offensive/flag/@type", "/types/offensive/flag", "");
            Funcs.fillListType(lstImmune, "/types/immune/flag/@type", "/types/immune/flag", "");
            Funcs.fillListType(lstResistance, "/types/immune/flag/@type", "/types/immune/flag", "");
            Funcs.fillListType(lstVulnerable, "/types/immune/flag/@type", "/types/immune/flag", "");
            Funcs.fillListType(lstForm, "/types/formCorpse/flag/@type", "/types/formCorpse/flag", "");
            Funcs.fillListType(lstPart, "/types/parts/flag/@type", "/types/parts/flag", "");
            Funcs.fillComboType(cmbSize, "/types/size/flag/@type", "/types/size/flag", "");
            Funcs.fillCombo(cmbSex, "/types/gender/sex");
            Funcs.fillCombo(cmbMaterial, "/types/materials/material");

            fillAvailableProgs();

            txtGroup1.Text = ah.area.MinVnum > 0 ? String.Format("{0}", ah.area.MinVnum/100) : "00";

            if (existingVNum >= 0) {
                Mobile m = (from Mobile mob in ah.area.Mobs
                            where mob.VNum == existingVNum
                            select mob).Single();

                txtVNum.Text = m.VNum.ToString();
                txtKeywords.Text = m.Keywords;
                txtShort.Text = m.Name;
                txtLong.Text = m.Short;
                txtDesc.Text = m.Long.Replace("\n", Environment.NewLine);
                txtGroup2.Text = String.Format("{0}", m.MobGroup - (m.MobGroup/100)*100);

                if (m.Pierce > trkPierce.Maximum)
                    trkPierce.Maximum = m.Pierce;
                if (m.Bash > trkBash.Maximum)
                    trkBash.Maximum = m.Bash;
                if (m.Slash > trkSlash.Maximum)
                    trkSlash.Maximum = m.Slash;
                if (m.Magical > trkMagic.Maximum)
                    trkMagic.Maximum = m.Magical;
                if (m.Treasure > trkTreasure.Maximum)
                    trkTreasure.Maximum = m.Treasure;
                if (m.Align > trkAlignment.Maximum)
                    trkAlignment.Maximum = m.Align;
                if (m.DamageBonus > trkDamBonus.Maximum)
                    trkDamBonus.Maximum = m.DamageBonus;

                trkPierce.Value = m.Pierce;
                trkBash.Value = m.Bash;
                trkSlash.Value = m.Slash;
                trkMagic.Value = m.Magical;
                trkTreasure.Value = m.Treasure;
                trkAlignment.Value = m.Align;
                trkDamBonus.Value = m.DamageBonus;

                cmbDamDice.Text = String.Format("Level {0} - {1}", m.Level, m.DamageDice);
                cmbHitDice.Text = String.Format("Level {0} - {1}", m.Level, m.HitDice);
                cmbManaDice.Text = String.Format("Level {0} - {1}", m.Level, m.ManaDice);
                cmbLevel.SelectedIndex = m.Level - 1;
                cmbClass.SelectedIndex = m.Class;
                cmbCast.SelectedIndex = m.CastLevel;
                cmbRace.SelectedIndex = Funcs.getComboIndex(cmbRace, m.Race);
                cmbStart.SelectedIndex = Funcs.getComboIndex(cmbStart, m.StartPos);
                cmbDefault.SelectedIndex = Funcs.getComboIndex(cmbDefault, m.DefaultPos);
                cmbDamType.SelectedIndex = Funcs.getComboIndex(cmbDamType, m.DamageType);
                cmbSex.SelectedIndex = Funcs.getComboIndex(cmbSex, m.Sex);
                cmbSize.SelectedIndex = Funcs.getComboIndex(cmbSize, m.Size.ToString());
                cmbMaterial.SelectedIndex = Funcs.getComboIndex(cmbMaterial, m.Material);

                Funcs.selectKeys(lstAct, m.Action);
                Funcs.selectKeys(lstAffect, m.Affects);
                Funcs.selectKeys(lstAff2, m.MoreAffects);
                Funcs.selectKeys(lstOffensive, m.Offensive);
                Funcs.selectKeys(lstImmune, m.Immune);
                Funcs.selectKeys(lstResistance, m.Resist);
                Funcs.selectKeys(lstVulnerable, m.Vulnerable);
                Funcs.selectKeys(lstForm, m.Form);
                Funcs.selectKeys(lstPart, m.Part);

                IOrderedEnumerable<MobProgLink> query = from MobProgLink mpl in m.Progs
                                                        orderby mpl.Program
                                                        select mpl;

                foreach (MobProgLink mpl in query) {
                    lstInstalledProgs.Items.Add(String.Format("#{0} - {1} ({2})", mpl.Program, mpl.Trigger, mpl.Argument));

                    lstProgsAvailable.Items.RemoveAt(Funcs.getListIndex(lstProgsAvailable,
                                                                        String.Format("#{0}", mpl.Program)));
                }

                btnClone.Enabled = true;
            } else {
                if (getNextVnum() < 0) {
                    MessageBox.Show("VNum Range was incremented by 100.", "Hrm...", MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    ah.area.MaxVnum += 100;
                }

                txtVNum.Text = getNextVnum().ToString();

                cmbLevel.SelectedIndex = 0;
                cmbCast.SelectedIndex = 0;
            }
        }

        private int getNextVnum() {
            int start = ah.area.MinVnum;
            int end = ah.area.MaxVnum;

            for (int i = start; i < end + 1; i++) {
                if (!validMobVnum(i))
                    return i;
            }

            return -1;
        }

        private bool validMobVnum(int vnum) {
            try {
#pragma warning disable 168
                Mobile mob = (from Mobile m in ah.area.Mobs
                              where m.VNum == vnum
                              select m).Single();
#pragma warning restore 168

                return true;
            } catch (InvalidOperationException) {}

            return false;
        }

        private void trkAlignment_ValueChanged(object sender, EventArgs e) {
            lblAlignVal.Text = trkAlignment.Value.ToString();
        }

        private static void changeColor(TabControl control, Color color) {
            for (int i = 0; i < control.TabCount; i++)
                control.TabPages[i].BackColor = color;
        }

        private void trkDamBonus_ValueChanged(object sender, EventArgs e) {
            lblDamBonusValue.Text = trkDamBonus.Value.ToString();
        }

        private void trkPierce_ValueChanged(object sender, EventArgs e) {
            lblPierceValue.Text = trkPierce.Value.ToString();
        }

        private void trkBash_ValueChanged(object sender, EventArgs e) {
            lblBashValue.Text = trkBash.Value.ToString();
        }

        private void trkSlash_ValueChanged(object sender, EventArgs e) {
            lblSlashValue.Text = trkSlash.Value.ToString();
        }

        private void trkMagic_ValueChanged(object sender, EventArgs e) {
            lblMagicValue.Text = trkMagic.Value.ToString();
        }

        private void trkTreasure_ValueChanged(object sender, EventArgs e) {
            if (trkTreasure.Value > 100) {
                int gold = trkTreasure.Value/100;
                lblTreasureValue.Text = String.Format("{0}g {1}s", gold, trkTreasure.Value - (gold*100));
            } else
                lblTreasureValue.Text = String.Format("{0}s", trkTreasure.Value);
        }

        private void cmbHitDice_Leave(object sender, EventArgs e) {
            var re = new Regex(@"^(?:Level \d+ \- )?\d+d\d+(?:\+\d+)?$");
            Match m = re.Match(cmbDamDice.Text);

            if (!m.Success) {
                MessageBox.Show("That's an invalid die! Die format is XXXdYYY+ZZZ, where X/Y/Z are numbers.", "Whoops!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                cmbHitDice.Focus();
                cmbHitDice.Text = "";
            }
        }

        private void cmbManaDice_Leave(object sender, EventArgs e) {
            var re = new Regex(@"^(?:Level \d+ \- )?\d+d\d+(?:\+\d+)?$");
            Match m = re.Match(cmbDamDice.Text);

            if (!m.Success) {
                MessageBox.Show("That's an invalid die! Die format is XXXdYYY+ZZZ, where X/Y/Z are numbers.", "Whoops!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                cmbManaDice.Focus();
                cmbManaDice.Text = "";
            }
        }

        private void cmbDamDice_Leave(object sender, EventArgs e) {
            var re = new Regex(@"^(?:Level \d+ \- )?\d+d\d+(?:\+\d+)?$");
            Match m = re.Match(cmbDamDice.Text);

            if (!m.Success) {
                MessageBox.Show("That's an invalid die! Die format is XXXdYYY+ZZZ, where X/Y/Z are numbers.", "Whoops!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                cmbDamDice.Focus();
                cmbDamDice.Text = "";
            }
        }

        private void lstInstalledProgs_SelectedIndexChanged(object sender, EventArgs e) {
            if (lstInstalledProgs.SelectedIndex < 0)
                return;

            var re = new Regex(@"^#(?<vnum>\d+) \- (?<trig>\w+) \((?<arg>.*)\)$");
            Match m = re.Match(lstInstalledProgs.Items[lstInstalledProgs.SelectedIndex].ToString());

            if (m.Success) {
                cmbProgType.SelectedIndex = Funcs.getComboIndex(cmbProgType, m.Groups["trig"].Value);
                txtProgArgument.Text = m.Groups["arg"].Value;
            }
        }

        private void btnOK_Click(object sender, EventArgs e) {
            SaveMobile();

            Dispose();
        }

        private Mobile SaveMobile() {
            Mobile m = existingVNum >= 0 ? (from Mobile mob in ah.area.Mobs
                                            where mob.VNum == existingVNum
                                            select mob).Single() : new Mobile();

            m.VNum = Int32.Parse(txtVNum.Text);
            m.Keywords = txtKeywords.Text;
            m.Name = txtShort.Text;
            m.Short = txtLong.Text;
            m.Long = txtDesc.Text;
            m.Race = cmbRace.SelectedItem.ToString();
            m.Level = Int32.Parse(cmbLevel.SelectedItem.ToString());
            m.Class = Int32.Parse(Funcs.getComboFlag(cmbClass));
            m.Action = Funcs.getFlags(lstAct);
            m.Align = trkAlignment.Value;

            if (String.IsNullOrEmpty(txtGroup2.Text))
                txtGroup2.Text = "00";

            m.MobGroup = Int32.Parse(String.Format("{0}{1}", txtGroup1.Text, txtGroup2.Text));
            m.CastLevel = Int32.Parse(cmbCast.SelectedItem.ToString());
            m.StartPos = cmbStart.SelectedItem.ToString();
            m.DefaultPos = cmbDefault.SelectedItem.ToString();
            m.DamageDice = getDice(cmbDamDice);
            m.DamageBonus = trkDamBonus.Value;
            m.DamageType = cmbDamType.SelectedItem.ToString();
            m.HitDice = getDice(cmbHitDice);
            m.ManaDice = getDice(cmbManaDice);
            m.Affects = Funcs.getFlags(lstAffect);
            m.MoreAffects = Funcs.getFlags(lstAff2);
            m.Offensive = Funcs.getFlags(lstOffensive);
            m.Immune = Funcs.getFlags(lstImmune);
            m.Resist = Funcs.getFlags(lstResistance);
            m.Vulnerable = Funcs.getFlags(lstVulnerable);
            m.Form = Funcs.getFlags(lstForm);
            m.Part = Funcs.getFlags(lstPart);
            m.Pierce = trkPierce.Value;
            m.Bash = trkBash.Value;
            m.Slash = trkSlash.Value;
            m.Magical = trkMagic.Value;
            m.Sex = cmbSex.SelectedItem.ToString();
            m.Treasure = trkTreasure.Value;
            m.Size = Int32.Parse(Funcs.getComboFlag(cmbSize));
            m.Material = cmbMaterial.SelectedItem.ToString();

            m.Progs.Clear();

            for (int i = 0; i < lstInstalledProgs.Items.Count; i++) {
                var re = new Regex(@"^#(?<vnum>\d+) \- (?<trigger>\w+) \((?<arg>.+)\)$");
                Match mm = re.Match(lstInstalledProgs.Items[i].ToString());

                if (mm.Success) {
                    var mpl = new MobProgLink {
                        Program = Int32.Parse(mm.Groups["vnum"].Value),
                        Trigger = mm.Groups["trigger"].Value,
                        Argument = mm.Groups["arg"].Value
                    };

                    m.Progs.Add(mpl);
                }
            }

            if (existingVNum < 0)
                ah.area.Mobs.Add(m);

            return m;
        }

        private void btnCancel_Click(object sender, EventArgs e) {
            Dispose();
        }

        private void fillAvailableProgs() {
            lstProgsAvailable.Items.Clear();

            IOrderedEnumerable<MobProg> query = from MobProg mp in ah.area.Progs
                                                orderby mp.VNum
                                                select mp;

            foreach (MobProg mp in query)
                lstProgsAvailable.Items.Add(String.Format("#{0} - {1}", mp.VNum, mp.Program));

            removeInstalledProgs();
        }

        private void removeInstalledProgs() {
            for (int i = 0; i < lstInstalledProgs.Items.Count; i++) {
                var re = new Regex(@"^#(?<vnum>\d+) \- (?<trig>\w+) \((?<arg>.*)\)$");
                Match m = re.Match(lstInstalledProgs.Items[i].ToString());

                int index = Funcs.getListIndex(lstProgsAvailable, String.Format("#{0}", m.Groups["vnum"]));

                if (index >= 0)
                    lstProgsAvailable.Items.RemoveAt(index);
            }
        }

        private void btnDel_Click(object sender, EventArgs e) {
            if (lstInstalledProgs.SelectedIndex >= 0) {
                lstInstalledProgs.Items.RemoveAt(lstInstalledProgs.SelectedIndex);

                fillAvailableProgs();
            } else
                MessageBox.Show("You need to select a mobprog to delete!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
        }

        private void btnAdd_Click(object sender, EventArgs e) {
            if (lstProgsAvailable.SelectedIndex >= 0) {
                if (cmbProgType.SelectedIndex < 0 || String.IsNullOrEmpty(txtProgArgument.Text)) {
                    MessageBox.Show("You need to select a prog type *and* argument!", "Whoops!", MessageBoxButtons.OK,
                                    MessageBoxIcon.Exclamation);

                    return;
                }

                var re = new Regex(@"^#(?<vnum>\d+) \-");
                Match m = re.Match(lstProgsAvailable.Items[lstProgsAvailable.SelectedIndex].ToString());

                lstInstalledProgs.Items.Add(String.Format("#{0} - {1} ({2})", m.Groups["vnum"].Value,
                                                          cmbProgType.Items[cmbProgType.SelectedIndex],
                                                          txtProgArgument.Text));

                fillAvailableProgs();
            } else
                MessageBox.Show("You need to select a mobprog to add!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
        }

        private static String getDice(ComboBox combo) {
            var re = new Regex(@"Level \d+ \- (?<dice>.+)");
            Match m = re.Match(combo.SelectedItem.ToString());

            if (m.Success)
                return m.Groups["dice"].Value;

            return "0d0+0";
        }

        private void btnPrev_Click(object sender, EventArgs e) {
            try {
                int currVnum = Int32.Parse(txtVNum.Text);
                IEnumerable<int> query = from Mobile mob in ah.area.Mobs
                                         where mob.VNum < currVnum
                                         orderby mob.VNum descending
                                         select mob.VNum;
                int newArray = query.First();

                Form newForm = new frmMobs(ah, newArray) { MdiParent = MdiParent };

                newForm.Location = new Point((MdiParent.Size.Width - newForm.Size.Width)/2 + 40,
                                             (MdiParent.Size.Height - newForm.Size.Height)/2 - 60);
                newForm.Show();

                btnOK_Click(null, null);
            } catch (InvalidOperationException) {
                MessageBox.Show("There are no more mobs to show!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
            }
        }

        private void btnNext_Click(object sender, EventArgs e) {
            try {
                int currVnum = Int32.Parse(txtVNum.Text);
                IEnumerable<int> query = from Mobile mob in ah.area.Mobs
                                         where mob.VNum > currVnum
                                         orderby mob.VNum ascending
                                         select mob.VNum;
                int newArray = query.First();

                Form newForm = new frmMobs(ah, newArray) { MdiParent = MdiParent };

                newForm.Location = new Point((MdiParent.Size.Width - newForm.Size.Width)/2 + 40,
                                             (MdiParent.Size.Height - newForm.Size.Height)/2 - 60);
                newForm.Show();

                btnOK_Click(null, null);
            } catch (InvalidOperationException) {
                MessageBox.Show("There are no more mobs to show!", "Whoops!", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
            }
        }

        private void btnClone_Click(object sender, EventArgs e) {
            Mobile m = SaveMobile().Clone();
            m.VNum = getNextVnum();
            m.Progs.Clear();

            ah.area.Mobs.Add(m);

            MessageBox.Show("Mobile was sucessfully cloned!", "Yay!", MessageBoxButtons.OK, MessageBoxIcon.Information);

            Form frmNew = new frmMobs(ah, m.VNum) { MdiParent = MdiParent };

            frmNew.Location = new Point((MdiParent.Size.Width - frmNew.Size.Width)/2 + 40,
                                        (MdiParent.Size.Height - frmNew.Size.Height)/2 - 60);
            frmNew.Show();

            Dispose();
        }
    }
}